﻿using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace SandCastleRunner
{
    /// <summary>
    /// Represents a project.
    /// </summary>
    public class Project
    {
        public const string documentationFileName = @"\comments.xml";
        public const string binDirectoryPath = @"\bin\Debug";
        public const string pathTopRegex = "[\\\\][^\\\\]+$";

        private string projectDirectoryPath;
        private string mainProjectPe;
        private string documentationRootDirectory;
        private string productionTransformXsl;
        private string tableOfContentsXsl;
        private string presentation;

        /// <summary>
        /// Creates a new instance of ProjectDirectory.
        /// </summary>
        public Project() { }

        /// <summary>
        /// Creates a new instance of ProjectDirectory.
        /// </summary>
        /// <param name="directoryName">The directory name.</param>
        public Project(string directoryPath)
        {
            if(directoryPath == null)
                throw new ArgumentNullException("directoryPath");

            ProjectDirectoryPath = directoryPath;
        }

        /// <summary>
        /// Sets the directory name of the project.
        /// </summary>
        [XmlAttribute]
        public string ProjectDirectoryPath
        {
            get { return projectDirectoryPath; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("DirectoryPath");

                if (! Directory.Exists(value))
                    throw new ProjectException("The directory " + value + " does not exist.");

                projectDirectoryPath = value;

                if(!HasDocumentation)
                    throw new ProjectException("The project " + projectDirectoryPath + " has no documentation file.");

                if (!HasDebug)
                    throw new ProjectException("The project " + projectDirectoryPath + " has no debug directory.");

                if (mainProjectPe == null)
                    return;

                if(!HasMainPe)
                    throw new ProjectException("The project " + projectDirectoryPath + " is missing " + mainProjectPe + ".");
            }
        }

        /// <summary>
        /// The main portable executable of the project
        /// </summary>
        [XmlAttribute]
        public string MainProjectPe
        {
            get { return mainProjectPe; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("MainProjectPe");

                mainProjectPe = value;

                if (projectDirectoryPath == null)
                    return;

                if (!HasDebug)
                    throw new ProjectException("The project " + projectDirectoryPath + " has no debug directory.");

                if(!HasMainPe)
                    throw new ProjectException("The project " + projectDirectoryPath + " is missing " + mainProjectPe + ".");
            }
        }

        /// <summary>
        /// The Production Transform XSL for the project documentation.
        /// </summary>
        [XmlAttribute]
        public string ProductionTransformXsl
        {
            get { return productionTransformXsl; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("ProductionTransform");

                productionTransformXsl = value;
            }
        }

        /// <summary>
        /// The table of content XSL.
        /// </summary>
        [XmlAttribute]
        public string TableOfContentsXsl
        {
            get { return tableOfContentsXsl; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("TableOfContentsXsl");

                tableOfContentsXsl = value;
            }
        }

        /// <summary>
        /// The presentation.
        /// </summary>
        [XmlAttribute]
        public string Presentation
        {
            get { return presentation; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("Presentation");

                presentation = value;
            }
        }

        /// <summary>
        /// Sets the directory of the documenatation.
        /// </summary>
        [XmlIgnore]
        public string DocumentationRootDirectory
        {
            get { return documentationRootDirectory; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("DocumentationRootDirectory");

                documentationRootDirectory = value;
            }
        }

        /// <summary>
        /// Checks if the debug directory exists and has contents.
        /// </summary>
        public bool HasDebug
        {
            get
            {
                if (projectDirectoryPath == null)
                    throw new InvalidOperationException("No project directory.");

                string debugDirectoryPath = projectDirectoryPath + binDirectoryPath;

                if (!Directory.Exists(debugDirectoryPath))
                    return false;

                string[] files = Directory.GetFiles(debugDirectoryPath);
                if (files.Length == 0)
                    return false;

                return true;
            }
        }

        /// <summary>
        /// Check if the main PE exists.
        /// </summary>
        public bool HasMainPe
        {
            get
            {
                if (projectDirectoryPath == null)
                    throw new InvalidOperationException("No project directory.");

                if (mainProjectPe == null)
                    throw new InvalidOperationException("No main project pe.");

                return File.Exists(projectDirectoryPath + binDirectoryPath + @"\" + mainProjectPe);
            }
        }

        /// <summary>
        /// Checks if the documentation exists.
        /// </summary>
        public bool HasDocumentation
        {
            get
            {
                if (projectDirectoryPath == null)
                    throw new InvalidOperationException("No project directory.");

                string documentationPath = projectDirectoryPath + documentationFileName;
                return File.Exists(documentationPath);
            }
        }

        /// <summary>
        /// Gets the project name.
        /// </summary>
        public string ProjectName
        {
            get
            {
                if (projectDirectoryPath == null)
                    throw new InvalidOperationException("No project directory.");

                Match pathTop = Regex.Match(projectDirectoryPath, pathTopRegex);
                return pathTop.Value.Substring(1);
            }
        }

        /// <summary>
        /// Gets the directory of the documentation.
        /// </summary>
        public string DocumentationDirectory
        {
            get
            {
                if (documentationRootDirectory == null)
                    throw new InvalidOperationException("No documentation root directory.");

                return documentationRootDirectory + @"\" + ProjectName;
            }
        }

        /// <summary>
        /// Checks if there is a subdirectory for the project in the documentation root directory.
        /// </summary>
        public bool HasDocumentationDirectory
        {
            get
            {
                if (documentationRootDirectory == null)
                    throw new InvalidOperationException("No documentation root directory.");

                return Directory.Exists(DocumentationDirectory);
            }
        }

        /// <summary>
        /// Prepares the documentation directory.
        /// </summary>
        public void PrepareDocumentationDirectory()
        {
            if (!HasDocumentationDirectory)
                Directory.CreateDirectory(DocumentationDirectory);

            if(!HasDocumentation)
                throw new InvalidOperationException("No documentation file.");

            CopyDocumentationFile();

            CopyPeFiles();
        }

        /// <summary>
        /// Copies the documentation file from the project
        /// directory to the documentation directory.
        /// </summary>
        private void CopyDocumentationFile()
        {
            string oldDocumentationFilePath = projectDirectoryPath + documentationFileName;
            string newDocumentationFilePath = DocumentationDirectory + documentationFileName;
            File.Copy(oldDocumentationFilePath, newDocumentationFilePath, true);
        }

        /// <summary>
        /// Copies the portable executable files from
        /// the debug directory to the documentation
        /// directory.
        /// </summary>
        private void CopyPeFiles()
        {
            string debugDirectoryPath = projectDirectoryPath + binDirectoryPath;
            string[] oldFilePaths = Directory.GetFiles(debugDirectoryPath);
            foreach (string oldFilePath in oldFilePaths)
            {
                string fileName = Regex.Match(oldFilePath, pathTopRegex).Value;
                string newFilePath = DocumentationDirectory + fileName;
                File.Copy(oldFilePath, newFilePath, true);
            }
        }
    }
}